package com.suray.wcs.service.core.util;

import com.suray.basic.wcs.plc.pojo.Robot;
import com.suray.basic.wcs.plc.utils.NetAddress;
import com.suray.wcs.service.pojo.Rgv;
import com.suray.wcs.service.util.WcsUtil;
import okio.BufferedSink;
import okio.BufferedSource;
import okio.Okio;
import okio.Timeout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author shaozn
 */

public class LongConnSendUtilLora {
    private static Logger log = LoggerFactory.getLogger(LongConnSendUtilLora.class);

    /**
     * 储存实例的容器
     */
    public static Map<Rgv, LongConnSendUtilLora> socketMap = new HashMap<>();
    public static Map<Robot, LongConnSendUtilLora> robotSocketMap = new HashMap<>();

    private Socket socket = null;
    private NetAddress netAddress;
    BufferedSink out = null;
    BufferedSource in = null;
    PrintWriter os = null;
    BufferedReader is = null;

    /**
     * 构造函数
     *
     * @param netAddress
     */
    private LongConnSendUtilLora(NetAddress netAddress) throws IOException {
        this.netAddress = netAddress;
        linkSocket();
    }

    /**
     * 构造函数
     *
     * @param netAddress
     */
    private LongConnSendUtilLora(NetAddress netAddress, Integer type) throws IOException {
        this.netAddress = netAddress;
        if(type == 2){
            linkSocketForString();
        }
    }

    private byte[] getReturnBytes() throws IOException {
        //定义最大数组
        byte[] buff = new byte[1024];
        int maxNum = in.read(buff);
        byte[] b = new byte[maxNum];
        System.arraycopy(buff, 0, b, 0, maxNum);
        return b;
    }

    private String getReturnString() throws IOException {
        String readLine = is.readLine();
        return readLine;
    }

    public static String recevie(Robot robot) throws IOException {
        LongConnSendUtilLora longConnSendUtil = getSocketSendUtil(robot);
        return longConnSendUtil.getReturnString();
    }

    public byte[] getReturnBytes(String des) throws IOException {
        //定义最大数组
        try {
            byte[] buff = new byte[1200];
            int maxNum = in.read(buff);
            byte[] b = new byte[maxNum];
            System.arraycopy(buff, 0, b, 0, maxNum);
            log.warn(des + ", 接收的报文：" + WcsUtil.changeToHexString(b));
            return b;
        } catch (IOException e) {
            terminate();
            throw e;
        }
    }

    public synchronized byte[] getReturnBytes(String des, Integer timeOut) throws IOException {
        //定义最大数组
        try {
            byte[] buff = new byte[1200];
            if (timeOut != null) {
                Timeout timeout = in.timeout();
                timeout.timeout(timeOut, TimeUnit.MILLISECONDS);
            }
            int maxNum = in.read(buff);

            if (maxNum < 0) {
                throw new IOException("通讯异常：readData size = " + maxNum);
            }

            byte[] b = new byte[maxNum];
            System.arraycopy(buff, 0, b, 0, maxNum);
            return b;
        } catch (IOException e) {
            terminate();
            throw e;
        }
    }

    public static byte[] send(byte[] bs, Rgv rgv) throws IOException {
        LongConnSendUtilLora longConnSendUtil = getSocketSendUtil(rgv);
        return longConnSendUtil.send(bs);
    }

    public static String send(String str, Robot robot) throws IOException {
        LongConnSendUtilLora longConnSendUtil = getSocketSendUtil(robot);
        return longConnSendUtil.sendForString(str);
    }

    /**
     * 发送
     *
     * @param bs
     * @return
     */
    public void send(byte[] bs, String des) throws IOException {
        try {
            linkSocket();
            out.write(bs);
            out.flush();
        } catch (Exception e) {
            terminate();
            throw new IOException();
        }
    }

    /**
     * 发送
     *
     * @param bs
     * @return
     */
    public void send(byte[] bs, String des, int timeOut) throws IOException {
        log.warn(des + ", 发送的报文：" + WcsUtil.changeToHexString(bs));
        linkSocket();
        Timeout timeout = in.timeout();
        timeout.timeout(timeOut, TimeUnit.MILLISECONDS);
        out.write(bs);
        out.flush();
    }

    public static LongConnSendUtilLora getSocketSendUtil(Rgv rgv) throws IOException {
        LongConnSendUtilLora longConnSendUtil = socketMap.get(rgv);
        if (longConnSendUtil == null) {
            longConnSendUtil = new LongConnSendUtilLora(new NetAddress(rgv.getRgvDB().getRgvIp()
                    , rgv.getRgvDB().getRgvPort()));
            socketMap.put(rgv, longConnSendUtil);
        }
        return longConnSendUtil;
    }

    public static LongConnSendUtilLora getSocketSendUtil(Robot robot) throws IOException {
        LongConnSendUtilLora longConnSendUtil = robotSocketMap.get(robot);
        if (longConnSendUtil == null) {
            longConnSendUtil = new LongConnSendUtilLora(new NetAddress(robot.getIp()
                    , robot.getPort()), 2);
            robotSocketMap.put(robot, longConnSendUtil);
        }
        return longConnSendUtil;
    }

    private void linkSocket() throws IOException {
        if (socket == null || socket.isClosed()) {
            socket = new Socket(netAddress.getIp(), netAddress.getPort());
            out = Okio.buffer(Okio.sink(socket));
            in = Okio.buffer(Okio.source(socket));
        }
    }

    private void linkSocketForString() throws IOException {
        if (socket == null || socket.isClosed()) {
            socket = new Socket(netAddress.getIp(), netAddress.getPort());
            os = new PrintWriter(socket.getOutputStream());;
            is = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        }
    }

    public static void changeSocket(Rgv rgv) throws IOException {
        socketMap.remove(rgv);
        synchronized (LongConnSendUtilLora.class) {
            LongConnSendUtilLora longConnSendUtil = new LongConnSendUtilLora(new NetAddress(rgv.getRgvDB().getRgvIp()
                    , rgv.getRgvDB().getRgvPort()));
            socketMap.put(rgv, longConnSendUtil);
        }
    }

    public void terminate() throws IOException {
        if (in != null) {
            in.close();
        }
        if (out != null) {
            out.close();
        }
        if(is != null){
            is.close();
        }
        if(os != null){
            os.close();
        }
        if (socket != null) {
            socket.close();
        }
        in = null;
        out = null;
        is = null;
        os = null;
        socket = null;
    }

    /**
     * 发送
     *
     * @param bs
     * @return
     */
    private synchronized byte[] send(byte[] bs) throws IOException {
        try {
            linkSocket();
//            Timeout timeout = in.timeout();
//            timeout.timeout(5000, TimeUnit.MILLISECONDS);
            out.write(bs);
            out.flush();
            return getReturnBytes("", 5000);
        } catch (Exception e) {
            terminate();
            throw new IOException();
        }
    }

    /**
     * 发送
     *
     * @param bs
     * @return
     */
    private synchronized String sendForString(String bs) throws IOException {
        try {
            linkSocketForString();
            os.print(bs);
            os.flush();
            return getReturnString();
        } catch (Exception e) {
            terminate();
            throw new IOException(e);
        }
    }

}
